package com.jungle.base.service;

import com.github.dozermapper.core.Mapper;
import com.jungle.base.dao.ExtendedJpaRepository;
import com.jungle.base.domain.bo.BaseUserBo;
import com.jungle.base.entity.BasePo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

import java.util.Date;
import java.util.List;
import java.util.Optional;

@Slf4j
public abstract class AbstractService<P extends BasePo<I>, B, I> implements BaseService<P, B, I> {
    @Resource
    protected Mapper mapper;


    @Autowired
    protected ExtendedJpaRepository<P, I> repository;

    public P save(P entity) {
        return repository.save(entity);
    }

    public List<P> saveAll(Iterable<P> entities) {
        return repository.saveAll(entities);
    }

    @Override
    public P saveOrUpdate(B bo, BaseUserBo userBo) {
        I boId = getBoId(bo);
        if (boId == null) {
            log.info("saveOrUpdate: create new entity");
            P po = this.bo2po(bo, userBo);
            I newId = newPoId();
            po.setId(newId);
            this.repository.persist(po);
            return po;
        } else {
            Optional<P> optional = this.findById(boId);
            if (optional.isPresent()) {
                P po = optional.get();
                log.info("saveOrUpdate: update existing entity");
                updatePo(bo, userBo, po);
                po.setUpdateTime(new Date());
                po.setUpdateUserId(userBo.getId());
                po.setUpdateUserName(userBo.getName());

                return this.repository.merge(po);
            } else {
                log.info("saveOrUpdate: create new entity");
                P po = this.bo2po(bo, userBo);
                this.repository.persist(po);
                return po;
            }
        }
    }

    private P bo2po(B bo, BaseUserBo userBo) {
        P po = createPo(bo, userBo);
        po.setCreateTime(new Date());
        po.setCreateUserId(userBo.getId());
        po.setCreateUserName(userBo.getName());
        return po;
    }

    protected abstract I getBoId(B bo);

    protected abstract I newPoId();

    protected abstract P createPo(B bo, BaseUserBo userBo);

    //protected abstract void updatePo(B bo, BaseUserBo userBo, P po);
    //protected P createPo(B bo, BaseUserBo userBo);

    protected void updatePo(B bo, BaseUserBo userBo, P po){
        mapper.map(bo, po);
    }

    @Override
    public void deleteById(I id) {
        repository.deleteById(id);
    }

    @Override
    public void deleteAllById(Iterable<I> entities) {
        repository.deleteAllByIdInBatch(entities);
    }

    @Override
    public void delete(P entity) {
        repository.delete(entity);
    }

    @Override
    public void deleteAll(Iterable<P> entities) {
        repository.deleteAllInBatch(entities);
    }

    @Override
    public Optional<P> findById(I id) {
        return repository.findById(id);
    }

    @Override
    public List<P> findAll(Specification<P> spec) {
        return repository.findAll(spec);
    }

    @Override
    public Page<P> findAll(Specification<P> spec, Pageable pageable) {
        return repository.findAll(spec, pageable);
    }

    @Override
    public List<P> findAll(Specification<P> spec, Sort sort) {
        return repository.findAll(spec, sort);
    }

    @Override
    public List<P> findAllById(Iterable<I> ids) {
        return repository.findAllById(ids);
    }
}
